//@author A0083782W
#include "UserCommand.h"

UserCommand::UserCommand() {
	reset();
	_myExpectation = Expection::NORMAL;
}


void UserCommand::reset() {
	_myNumberList.clear();
	_myNumberList.shrink_to_fit();
	_messages.clear();
	_messages.shrink_to_fit();
	_myEvent.reset();
	_myInputPrompt = NULL_STRING;
	_myOptType = OperationType::UNCLEAR;
	_currentStatus = IllegalInput::NO_PROBLEM;
	_myNumber = 0;
	_mySearchInfo.reset();
	SingleAction::reset();
}

void UserCommand::analyse (con_str& userInput) {
	try {
		reset();
		generateCommand (userInput);
		giveMeToOperations (this);
	}
	catch (ThrowID) {
		throw;
	}
}

void UserCommand::generateCommand(con_str& userInput) {
	try { 
		if (_myExpectation == Expection::NORMAL) {
			OperationType testType = OperationType::UNCLEAR;
			_myInputPrompt = userInput;
			string keyWord;
			string parameter;
			chopKeyWords (_myInputPrompt, keyWord, parameter);

			vector<string> stringList;
			vector<pos_int> indicesList;
			if ( keyWord == NULL_STRING) {
				_currentStatus = IllegalInput::UNKNOW_CHARACTERS;
				return ;
			}
			testType = mapKeyWord (keyWord);

			switch (testType) {
			case OperationType::UNCLEAR:
				goto LABEL_IntelligentSense;
				break;
			case OperationType::ADD:
				cutWordsFromString (parameter, CMD_SEP_CHAR, CMD_SEP_CHAR_N, stringList);
				if (stringList.size() > ADD_PARAMETERS_SIZE ) {
					_currentStatus = IllegalInput::ADD_TOO_MANY_PARAMETERS;
					return;
				}
				while (stringList.size() < ADD_PARAMETERS_SIZE ) {
					stringList.push_back (NULL_STRING);
				}
				analyseAdd (stringList);
				break;
			case OperationType::DELETE_BY_NUMBER:
				splitString (parameter, DEL_SEP_CHAR_LIST, DEL_SEP_CHAR_LIST_N, stringList, indicesList);
				analyseDelete (stringList);
				break;
			case OperationType::MODIFY:
				cutWordsFromString (parameter, CMD_SEP_CHAR, CMD_SEP_CHAR_N, stringList);
				analyseModify (stringList);
				break;
			case OperationType::SEARCH:
				analyseSearch (parameter);
				break;
			case OperationType::UNDO:
				analyseUndo(parameter);
				break;
			case OperationType::MARK:
				analyseMark (parameter);
				break;
			case OperationType::HELP:
				analyseHelp (parameter);
				break;
			case OperationType::CONFIG:
				_myOptType = OperationType::CONFIG;
				break;
			case OperationType::CLEAR:
				_myOptType = OperationType::CLEAR;
				break;
			case OperationType::EXIT:
				_myOptType = OperationType::EXIT;
				break;
			case OperationType::SAVEFILE:
				_myOptType = OperationType::SAVEFILE;
				break;
			default:
		LABEL_IntelligentSense:
				analyseAutoSense(userInput);
				break;
			}
		}
		else if (_myExpectation == Expection::CONFIG_STEP2) {   //ooooo
		    _myNumber = (atoi(userInput.c_str()));
		}
		else if (_myExpectation == Expection::CONFIG_STEP3) { //ooooo
            _myNumber = (atoi(userInput.c_str()));
		}
	}
	catch (ThrowID) {
		throw;
	}
}

void UserCommand::analyseModify (const vector<string>& parameters) {
	_myModifyInfo.reset();
	vector<string> stringList;
	vector<pos_int> intList;

	if (parameters.size() == 0) {
		_currentStatus = IllegalInput::MODIFY_NO_INDEX;
		return;
	}
	splitString(parameters[0], DEL_SEP_CHAR_LIST,DEL_SEP_CHAR_LIST_N,stringList,intList);
	for (auto i:stringList) {
		getNumberList (i,_myModifyInfo.IDs);
	}
	if (_myModifyInfo.IDs.size() == 0) {
		_currentStatus = IllegalInput::MODIFY_NO_INDEX;
		return;
	}
	
	for (pos_int i = 1; i < ADD_PARAMETERS_SIZE + 1; i++) {
		if (i > parameters.size() - 1) {
			_myModifyInfo.modifyInfo.push_back (NULL_STRING);
		}
		else {
			_myModifyInfo.modifyInfo.push_back (parameters[i]);
		}
	}

	_myOptType = OperationType::MODIFY;
}

flexiReturnPtr UserCommand::getInput() const {
	if (_currentStatus == IllegalInput::NO_PROBLEM) {
		if (_myExpectation == Expection::NORMAL) {
			switch (_myOptType) {
			case OperationType::UNCLEAR:
				break;
			case OperationType::ADD:
				return (void*) (&_myEvent);
				break;
			case OperationType::DELETE_BY_NUMBER:
				return (void*) (&_myNumberList);
				break;
			case OperationType::MODIFY:
				return (void*)(&_myModifyInfo);
				break;
			case OperationType::SEARCH:
				return (void*)(&_mySearchInfo);
				break;
			case OperationType::UNDO:
				return (void*)(&_myNumberList);
				break;
			case OperationType::HELP:
				return (void*)(&_myHelpNeed);
				break;
			case OperationType::CONFIG:
				break;
			case OperationType::MARK:
				return (void*)(&_markInfo);
				break;
			case OperationType::EXIT:
				break;
			default:
				break;
			}
		}
		else if (_myExpectation == Expection::CONFIG_STEP2 || 
			_myExpectation == Expection::CONFIG_STEP3) {
			return (void*) (& _myNumber);
		}
	}
	else {
		failProgram (ERR_UserCommand::illegal_input_error);
	}
	return nullptr;
}

void UserCommand::analyseUndo (con_str& parameter) {
	_myOptType = OperationType::UNDO;
	if (parameter == NULL_STRING) {
		_myNumberList.push_back (1);
		return;
	}

	if (isNumString(parameter)) {
		const pos_int temp = atoi(parameter.c_str());
		_myNumberList.push_back (temp);
	}
	else if (isIllegalString(parameter,SEP_CHAR_LIST,SEP_CHAR_LIST_N)) {
		_myNumberList.push_back (1);
	}
	else {
		_currentStatus = IllegalInput::UNDO_ILLEAGAL_NUMBER;
	}
}
void UserCommand::analyseHelp (con_str& parameter) {
	vector<string> strList;
	vector<pos_int> intList;
	splitString (parameter, SEP_CHAR_LIST, SEP_CHAR_LIST_N, strList, intList);
	for (auto i: strList) {
		if (mapKeyWord (i) != OperationType::UNCLEAR) {
			_myHelpNeed = mapKeyWord (i);
			goto analyseHelpEnd;
		}
	}
	_myHelpNeed = OperationType::UNCLEAR;
analyseHelpEnd:
	_myOptType = OperationType::HELP;
}

void UserCommand::setExpectation (Expection newExpectation) {
	_myExpectation = newExpectation;
}

Expection UserCommand::getExpectation() const {
	return _myExpectation;
}

//message|time|reminder|repeat|flag
void UserCommand::analyseAdd(const vector<string>& parameters) {
	try {
		WordList newAnalyser;
		Time testTimer1;
		Time testTimer2;
		
		if (parameters[0] != NULL_STRING ) {
			_myEvent.setDetails (parameters[0]);	
		}
		else {
			_currentStatus = IllegalInput::ADD_NO_TASK_DESCIPTION;
			return;
		}
	
		if (parameters[1] != NULL_STRING) {
			newAnalyser.setMessage(parameters[1]);
			newAnalyser.solveAll();
			newAnalyser.getStartEndTimer (testTimer1, testTimer2);
			_myEvent.setStartTime (testTimer1);
			if (newAnalyser.isDateStringDuration() || newAnalyser.isTimeStringDuration()) {
				_myEvent.setDurationType (true);
				_myEvent.setEndTime (testTimer2);
			}
			testTimer1.reset();
			testTimer2.reset();
		}

		if (parameters[2] != NULL_STRING) {
			newAnalyser.setMessage (parameters[2]);
			newAnalyser.solveAll();
			newAnalyser.getStartEndTimer (testTimer1, testTimer2);
			_myEvent.setRemindTime (testTimer1);
			testTimer1.reset();
			testTimer2.reset();
		}

		if (parameters[3] != NULL_STRING) {
			newAnalyser.setMessage (parameters[3]);
			newAnalyser.solveAll();
			_myEvent.setRepeatRule (newAnalyser.getRepeatRule());
		}

		if (parameters[4] != NULL_STRING) {
			vector<string>* wordListPtr;
			vector<pos_int> indicesList;
			wordListPtr = _myEvent.getTagPtr(); 
			splitString (parameters[4], TAG_SEP_CHAR, TAG_SEP_CHAR_N, *wordListPtr, indicesList);
		}

		_myOptType = OperationType::ADD;
	}
	catch (ThrowID) {
		throw;
	}
}

void UserCommand::analyseSearch (con_str& parameter) {
	_mySearchInfo.reset();
	vector<string> parameters;
	vector<string> tags;
	vector<pos_int> indexList;
	cutWordsFromString (parameter, CMD_SEP_CHAR, CMD_SEP_CHAR_N, parameters) ;
	WordList newAnalyser;
	Time timer1,timer2;
	CommonDataType::ByteInt i;

	switch (parameters.size()) {
	case 0:
		_mySearchInfo.filters.push_back (SearchFilter::ANYTHING);
		break;
	case 1:
		if (parameters[0] == NULL_STRING) {
			_mySearchInfo.filters.push_back (SearchFilter::ANYTHING);
		}
		else {
			_mySearchInfo.filters.push_back (SearchFilter::NORMAL);
			_mySearchInfo.messages = parameters[0];
		}
		break;
	case 2:
		_mySearchInfo.messages = parameters[0];
		newAnalyser.setMessage (parameters[1]);
		newAnalyser.solveAll();
		newAnalyser.getStartEndTimer(timer1,timer2);
		_mySearchInfo.beginTime = timer1;
		if (newAnalyser.isDateStringDuration()== false) {
			_mySearchInfo.endTime = timer1;
		}
		else {
			_mySearchInfo.endTime = timer2;
		}
		
		_mySearchInfo.filters.push_back (SearchFilter::NORMAL);
		break;
	default:
		_mySearchInfo.messages = parameters[0];
		newAnalyser.setMessage (parameters[1]);
		newAnalyser.solveAll();
		newAnalyser.getStartEndTimer(timer1,timer2);
		_mySearchInfo.beginTime = timer1;
		if (newAnalyser.isDateStringDuration()== false) {
			_mySearchInfo.endTime = timer1;
		}
		else {
			_mySearchInfo.endTime = timer2;
		}
		splitString (
			parameters[2], 
			SEP_CHAR_LIST,
			SEP_CHAR_LIST_N, 
			_mySearchInfo.tags, 
			indexList);
		for (i = 0; i < SEARCH_PARAMETER_SIZE; i++) {
			parameters.erase(parameters.begin());
		}
		analyseFilter(parameters, _mySearchInfo.filters);
		if (_mySearchInfo.filters.size() == 0) {
			_mySearchInfo.filters.push_back (SearchFilter::NORMAL);
		}
		break;
	}
	_myOptType = OperationType::SEARCH;
}

IllegalInput UserCommand::getError() const {
	return _currentStatus;
}

void UserCommand::analyseFilter (const vector<string>& parameters,vector<SearchFilter>& filterVector) {
	for (auto i:parameters) {
		vector<string> words;
		vector<pos_int> indexList;
		splitString (i, SEP_CHAR_LIST, SEP_CHAR_LIST_N, words, indexList);
		for (pos_int j = 0; j < words.size(); j++) {
			ignoreCase (&words[j]);
		}
		if (words.size() == SEARCH_FLAG_MAX_SIZE ) {
			if (words[0] == STRING_NOT) {
				if (words[1] == STRING_REMINDED) {
					filterVector.push_back (SearchFilter::NOT_REMINDED);
				}
				else if (words[1] == STRING_REPEATABLE) {
					filterVector.push_back (SearchFilter::NOT_REPEATABLE);
				}
				else if (words[1] == STRING_DONE) {
					filterVector.push_back (SearchFilter::NOT_DONE);
				}
				else if (words[1] == STRING_SFLOATING) {
					filterVector.push_back (SearchFilter::NOT_FLOATING);
				}
				else if (words[1] == STRING_MISSED) {
					filterVector.push_back (SearchFilter::NOT_MISSED);
				}
				else if (words[1] == STRING_FAVOURITE) {
					filterVector.push_back (SearchFilter::NOT_FAVOURITE);
				}
				else {
					goto analyseFilterReportError;
				}
			}
			else {
				goto analyseFilterReportError;
			}
		}
		else if (words.size() == SEARCH_FLAG_MIN_SIZE) {
			if (words[0] == STRING_REMINDED) {
				filterVector.push_back (SearchFilter::REMINDED);
			}
			else if (words[0] == STRING_REPEATABLE) {
				filterVector.push_back (SearchFilter::REPEATABLE);
			}
			else if (words[0] == STRING_DONE) {
				filterVector.push_back (SearchFilter::DONE);
			}
			else if (words[0] == STRING_SFLOATING) {
				filterVector.push_back (SearchFilter::FLOATING);
			}
			else if (words[0] == STRING_MISSED) {
				filterVector.push_back (SearchFilter::MISSED);
			}
			else if (words[0] == STRING_FAVOURITE) {
				filterVector.push_back (SearchFilter::FAVOURITE);
			}
			else {
				goto analyseFilterReportError;
			}
		}
		else {
analyseFilterReportError:
			_currentStatus = IllegalInput::SEARCH_ILLEGAL_FLAGS;
		}
	}
}

void UserCommand::analyseMark (con_str& parameter) {
	_markInfo.reset();
	vector<string> parameters;
	vector<pos_int> intList;
	cutWordsFromString (parameter, CMD_SEP_CHAR, CMD_SEP_CHAR_N, parameters);
	vector<string> numberList;
	if (parameter.size() == 0) {
		_currentStatus = IllegalInput::MARK_NO_INDEX;
		return;
	}
	splitString (parameters[0], DEL_SEP_CHAR_LIST, DEL_SEP_CHAR_LIST_N, numberList, intList);
	for (auto i:numberList) {
		getNumberList (i, _markInfo.IDs);
	}
	for (pos_int i = 1; i < parameters.size(); i++) {
		vector<string> aWord;
		ignoreCase (&parameters[i]);
		splitString (parameters[i], TAG_SEP_CHAR, TAG_SEP_CHAR_N, aWord, intList);
		
		if (aWord.size() == SEARCH_FLAG_MAX_SIZE) {
			if (aWord[0] == STRING_NOT) {
				if (aWord[1] == STRING_DONE) {
					_markInfo.filters.push_back (MarkFilter::NOT_DONE);
				}
				else if(aWord[1] == STRING_MISSED) {
					_markInfo.filters.push_back (MarkFilter::NOT_MISSED);
				}
				else if(aWord[1] == STRING_REMINDED) {
					_markInfo.filters.push_back (MarkFilter::NOT_REMINDED);
				}
				else if(aWord[1] == STRING_FAVOURITE) {
					_markInfo.filters.push_back (MarkFilter::NOT_FAVOURITE);
				}
			}
		}
		else if(aWord.size() == SEARCH_FLAG_MIN_SIZE) {
			if (aWord[0] == STRING_DONE) {
				_markInfo.filters.push_back (MarkFilter::DONE);
			}
			else if(aWord[0] == STRING_MISSED) {
				_markInfo.filters.push_back (MarkFilter::MISSED);
			}
			else if(aWord[0] == STRING_REMINDED) {
				_markInfo.filters.push_back (MarkFilter::REMINDED);
			}
			else if(aWord[0] == STRING_FAVOURITE) {
				_markInfo.filters.push_back (MarkFilter::FAVOURITE);
			}
		}
	}
	_myOptType = OperationType::MARK;
}

void UserCommand::analyseDelete (const vector<string>& parameter) {
	for (pos_int i = 0; i < parameter.size(); i++) {
		if (getNumberList(parameter[i], _myNumberList) == false) {
			_currentStatus = IllegalInput::DELETE_ILLEGAL_PARAMETER;
			return;
		}
	}
	if (_myNumberList.size() != 0 ) {
		_myOptType = OperationType::DELETE_BY_NUMBER;
	}
	else {
		_currentStatus = IllegalInput::DELETE_ILLEGAL_PARAMETER;
	}
}

void UserCommand::analyseAutoSense(con_str& mainString) {
	try {
		WordList newAnalyser;
		newAnalyser.setMessage (mainString);
		newAnalyser.solveAll();
		_myOptType = OperationType::ADD;
		_myEvent.setDetails ( _myInputPrompt);
		Time testTimer1;
		Time testTimer2;
		newAnalyser.getStartEndTimer (testTimer1, testTimer2);
		_myEvent.setStartTime (testTimer1);
		if (newAnalyser.isDateStringDuration() || newAnalyser.isTimeStringDuration()) {
			_myEvent.setDurationType (true);
			_myEvent.setEndTime (testTimer2);
		}
		_myEvent.setRepeatRule (newAnalyser.getRepeatRule());
	}
	catch (ThrowID) {
		throw;
	}
}

static void giveMeToOperations (UserCommand* aCommand) {
	Operations * ptr = &Operations::get();
	ptr->executeCommand (*aCommand);
}

namespace UserCommandStringFunctions {
	void chopKeyWords (con_str& mainString, string& keyWord,string& choppedString) {
		int i = 0;
		pos_int beginning = 0;
		pos_int ending = mainString.size() - 1;
		for (; i < (int)mainString.size(); i++ ) {
			if ( !(isEqualChar(mainString[i], SEP_CHAR_LIST, SEP_CHAR_LIST_N))) {
				beginning = i;
				break;
			}
		}

		if (i == (int)mainString.size()) {
			choppedString = NULL_STRING;
			return;
		}

		for (i = beginning; i < (int)mainString.size(); i++ ) {
			if (isEqualChar(mainString[i], SEP_CHAR_LIST, SEP_CHAR_LIST_N)) {
				ending = i - 1;
				break;
			}
		}

		keyWord = mainString.substr (beginning, ending - beginning + 1);
		if ((int)ending <= (int)((int)mainString.size() - 3)) {
			choppedString = mainString.substr (ending + 2, mainString.size());
		}
		else {
			choppedString = NULL_STRING;
		}

		for (i = 0; i <(int) choppedString.size(); i++ ) {
			if (isEqualChar (choppedString[i], SEP_CHAR_LIST, SEP_CHAR_LIST_N)) {
				choppedString.erase (i,1);
				i--;
			}
			else {
				break;
			}
		}
	}

	OperationType mapKeyWord (con_str& mainString ) {
		for (pos_int i = 1; i < KEYWORD_LIST_N; i++ ) {
			if (isEqualString(mainString, KEYWORD_LIST[i], CMD_MAX )) {
				return (OperationType)(i);
			}
		}
		return OperationType::UNCLEAR;
	}

	bool getNumberList (con_str& mainString, vector<pos_int>& numberList) {
		string temp = mainString;
		ignoreCase (&temp);
		filterChar (temp, DEL_PARAMETER_ALLOWED_CHAR_LIST, DEL_PARAMETER_ALLOWED_CHAR_LIST_N);
		if (isNumString (temp)) {
			numberList.push_back (atoi (temp.c_str()));
		}
		else {
			pos_int firstBeginning = 0;
			pos_int firstEnding = 0;
			pos_int lastBeginning = 0;
			pos_int lastEnding = temp.size() - 1;
			pos_int starting = 0;
			pos_int ending = 0;
			pos_int i = 0;
			for (; i < temp.size(); i++ ) {
				if (!isNumString (temp.substr (i,1))) {
					firstEnding = i - 1;
					break;
				}
			}
			for (; i < temp.size(); i++) {
				if (isNumString (temp.substr (i,1))) {
					lastBeginning = i;
					break;
				}
			}

			if (firstEnding - firstBeginning >= 0) {
				starting = atoi (
					temp.substr (
					firstBeginning, firstEnding - firstBeginning + 1
					).c_str());
			}
			else {
				return false;
			}
			if (lastEnding - lastBeginning >= 0) {
				ending = atoi (
					temp.substr (
					lastBeginning, lastEnding - lastBeginning + 1
					).c_str());
			}
			else {
				return false;
			}

			if ( starting > ending ) {
				return false;
			}
			else {
				for ( i = starting; i <= ending; i++) {
					numberList.push_back (i);
				}
			}
		}
		return true;
	}
};
